Background:
Let’s take a simple model of a three department hospital, with an Emergency Department (ED), an Intensive Care Unit (ICU), and a department for General (Gen) Admission for less severe injuries. We can perform as simulation over the course of a year and see how many patients we can cure, while minimizing the amount we have to transfer, and who pass away.
Simulations are generally function-heavy. This is because it allows us perform the same operation without unnecessary copy-pasting of code. So let’s put our initial set-up of variables, which have been more-or-less arbitrarily chosen, into a function
library(dplyr)
library(ggplot2)
library(gganimate)
initial_setup <<- function(){
ed_beds.i <<- 35 # Emergency Department beds available
full_ed_beds.i <<- 0 # Occupied Emergency Department beds
icu_beds.i <<- 50 # ICU Department beds available
full_icu_beds.i <<- 0 # Occupied ICU Department beds
gen_beds.i <<- 50 # General Department beds available
full_gen_beds.i <<- 0 # Occupied General Department beds
total_patients <<- 5000
final_day <<- 365 # final day of our simulation
day.v <<- seq(1:final_day)
}
initial_setup()
We’ll need a data frame to hold all of our patient data. For the sake of efficiency, we generate all of this data up front.
patient.df_setup <- function(){
entry_day.v <- sort(sample(0:final_day, total_patients, replace=TRUE)) # The day that the patient is injured
severity.v <- runif(n = total_patients, min = .1, max = 1) # The level of severity of patient injury
location.v <- rep("Home", total_patients) # Current location of patients
status.v <- rep("Healthy", total_patients) # other catagories will be Injured, Released, Deceased, Cured
placement.v <- ifelse(severity.v >= 0.8, "ED",
ifelse(severity.v > 0.6, "ICU",
"Gen")) # The initial placement of patient based on severity
df <- data.frame("Entry_day" = entry_day.v, "Severity" = severity.v,
"Placement" = placement.v, "Location" = location.v,
"Status" = status.v)
df <- df %>% mutate(Placement = as.character(Placement),
Location = as.character(Location),
Status = as.character(Status))
df <- df %>% arrange(Entry_day)
return(df)
}
patient.df <- patient.df_setup()
Now before we actually begin our simulation, let’s break it into pieces for the sake of maintainable code. This may look a bit strange at first, but it will all come together as we move toward the end.
The first function we’ll look at handles new patients entering the hospital. They are sorted into their appropriate department, and their status and location are adjusted appropriately.
entrance <- function(df, day){ # Entrance of new patients for the day
df <- df %>% mutate(Placement = ifelse(Severity >= 0.8, "ED", ifelse(Severity > 0.6, "ICU", "Gen")),
Status = ifelse((Entry_day == day) & (Location == "Home"),"Injured", Status),
Location = ifelse((Status == "Injured") & (Location == "Home"),Placement, Location)) %>%
arrange(Entry_day,Severity)
return(df)
}
Our second function is going to adjust our patient’s “Severity” value on a daily basis. The patient’s condition can improve, stay about the same, or deteriorate. The beta distribution has the appropriate qualities to fit our model, but it’s important to note that you could easily defend using many other distributions for this.
When we call this function later, we’re going to stay optimistic and say that patients will generally improve during their stay in the hospital.
improvements <- function(df, skew = "None"){ # Adjusts patient severity by a random value drawn from beta distribution
# Beta distribution parameters
if (skew == "Left" ){param1 = 40 # Left skewed --- Patient will generally improve
param2 = 50}
else if (skew == "Right"){param1 = 50 # Right skewed --- Patient will generally deteriorate
param2 = 40}
else {param1 = 45 # No skew --- Patient has an equal probability to improve/deteriorate
param2 = 45}
df <- df %>% mutate(Severity = ifelse( Location == "ED", Severity + (rbeta(1, param1, param2) - .5) * 1.5,
ifelse( Location == "ICU", Severity + (rbeta(1, param1, param2) - .5) * 1.25,
ifelse( Location == "Gen", Severity + (rbeta(1, param1, param2) - .5),
Severity))),
Location = ifelse( Severity <= 0, "Home", # Patient is cured and sent home.
ifelse( Severity >= 1, "Morgue",# Patient passes away and is sent to the morgue
Location)),
Status = ifelse( Severity <= 0, "Cured",
ifelse( Severity >= 1, "Deceased",
Status)))
return(df)
}
The third and fourth functions are simply going to see how many patients are in each location, and which status they are presenting. At the end of each day, these are put into a data frame for later visualization.
tally_beds <- function(df){ # gets current number of full beds
old_deceased.i <<- deceased.i
old_transferred.i <<- transferred.i
full_ed_beds.i <<- tally(df %>% filter(Location == "ED")) %>% as.integer()
full_icu_beds.i <<- tally(df %>% filter(Location == "ICU")) %>% as.integer()
full_gen_beds.i <<- tally(df %>% filter(Location == "Gen")) %>% as.integer()
deceased.i <<- tally(df %>% filter(Status == "Deceased")) %>% as.integer()
cured.i <<- tally(df %>% filter(Status == "Cured")) %>% as.integer()
healthy.i <<- tally(df %>% filter(Status == "Healthy")) %>% as.integer()
transferred.i <<- tally(df %>% filter(Status == "Transferred")) %>% as.integer()
healhty.i <<- tally(df %>% filter(Status == "Healthy")) %>% as.integer()
}
day_end_tally <- function(df, day){
tally_beds(df)
full_beds.df <<- rbind(full_beds.df, data.frame(Day = day,
ED = full_ed_beds.i,
ICU = full_icu_beds.i,
Gen = full_gen_beds.i,
Healthy = healthy.i,
Cured = cured.i,
Deceased = deceased.i,
Transferred = transferred.i))
}
The rerank function simply gives our lowest severity patients (per group) a rank closer to zero. Why do we need that? Because the hospital, when faced with space constraints, will try to release or transfer their lowest severity patients (with some caveats below).
rerank <- function (df){ # Ranks patient Severity by location --- lowest Severity gets lowest number rank
df <- df %>% group_by(Location) %>%
mutate(Rank = ifelse(Location %in% c("ED","ICU","Gen"), rank(Severity, ties.method = "first"),NA)) %>%
ungroup()
tally_beds(df)
return(df)
}
Below is our main function. It simulates one day by having new patients enter, transferring patients as needed, tallying up the beds, adjusting our patient’s severity each day, and then the function will recursively call itself until it hits the end of the simulation.
For the sake of the model, we only allow transfers to other departments if their severity is on the border between the established points. For example, a patient must be between severity level 0.8 and 0.85 to be considered for transfer from the Emergency Department to the ICU. If the hospital still can’t handle the number of patients, they are transferred to another hospital.
next_day <- function(df, day, final_day){ # Recursive function --- reassigns patients to new area if beds full
df <- entrance(df, day)
df <- rerank(df)
# reassign ED patients to ICU department if ED is full if possible
# if patient is too severe for ICU - they are transfered to another hospital
df <- df %>% mutate(Location = ifelse((Location == "ED") & (Rank <= (full_ed_beds.i - ed_beds.i)),
ifelse(Severity < 0.85,"ICU","Another Hospital"), Location))
df <- rerank(df)
# reassign ICU patients to Gen departmentt if ICU is full
# if patient is too severe for General admission - they are transfered to another hospital
df <- df %>% mutate(Location = ifelse((Location == "ICU") & (Rank <= (full_icu_beds.i - icu_beds.i)),
ifelse(Severity < 0.65,"Gen","Another Hospital"), Location))
df <- rerank(df)
# relases patients if beds are full and their severity is low enough
# otherwise transfers them to another hospital
df <- df %>% mutate(Location = ifelse((Location == "Gen") & (Rank <= (full_gen_beds.i - gen_beds.i)),
ifelse(Severity < 0.20,"Released","Another Hospital"), Location),
Status = ifelse(Location == "Another Hospital", "Transferred", Status),
Status = ifelse(Location == "Released", "Released", Status))
day_end_tally(df,day)
df <- improvements(df, skew = "Left") # Adjusts patients severity for the day
day = day + 1
if(day <= final_day){
next_day (df, day, final_day) # Move on to next day if not past the final day
}
else {return(df)}
}
And our last function doing any analytic work will call the functions above and give us our data in a nice format for our simulation visualization.
bed_count <- function(){
full_beds.df <<- data.frame(Day = integer(),
ED = integer(),
ICU = integer(),
Gen = integer(),
Healthy = integer(),
Cured = integer(),
Deceased = integer(),
Transffered = integer(),
Healthy = integer())
deceased.i <<- 0
transferred.i <<- 0
patient.df <<- next_day(patient.df, 0, final_day)
tally_beds(patient.df)
}
bed_count()
full_beds.df
Above, in tabular form, we can see how our departments and statuses of our patients change each day. But there is only so much data that can be absorbed through a table. In order for these data to be meaningful, we have to understand it on a deeper level than can be conveyed by a static number on a page.
And for that, we use visualizations. Using the gganimate package, and the data generated above, we can show how the composition of our hospital changes over the course of a year.
plot_full_beds <- function(){
options(gganimate.nframes = max(full_beds.df$Day)*2)
full_beds.gg <-ggplot(full_beds.df) +
geom_point(aes(2, -0.3, size = Healthy), color = "royalblue") +
geom_point(aes(1, 1, size = ED), color = "deepskyblue") +
geom_point(aes(2, 1, size = ICU), color = "deepskyblue") +
geom_point(aes(3, 1, size = Gen), color = "deepskyblue") +
geom_point(aes(2.5, 3, size = Deceased), color = "steelblue") +
geom_point(aes(2, 2, size = Transferred), color = "steelblue") +
geom_point(aes(1.5, 3, size = Cured), color = "steelblue") +
geom_text(aes(2, -0.5, label = paste("Healthy\n", Healthy))) +
geom_text(aes(1, 0.7, label = paste("ED\n", ED))) +
geom_text(aes(2, 0.7, label = paste("ICU\n", ICU))) +
geom_text(aes(3, 0.7, label = paste("Gen\n", Gen))) +
geom_text(aes(2.5, 2.7, label = paste("Deceased\n", Deceased))) +
geom_text(aes(2, 1.7, label = paste("Transferred\n", Transferred))) +
geom_text(aes(1.5, 2.7, label = paste("Cured\n", Cured))) +
transition_states(Day,
transition_length = 2,
state_length = 2) +
scale_size(range = c(1,80)) +
theme_bw() +
theme(axis.line=element_blank(),
axis.text.x=element_blank(),
axis.text.y=element_blank(),
axis.ticks=element_blank(),
axis.title.x=element_blank(),
axis.title.y=element_blank(),
legend.position="none",
panel.background=element_blank(),
panel.border=element_blank(),
panel.grid.minor=element_blank()) +
xlim(c( 0.5, 3.5)) +
ylim(c( -1, 3.5)) +
ggtitle("Day {closest_state}") +
theme()
print(full_beds.gg)
anim_save("Hospital_simulation.gif", full_beds.gg)
}
plot_full_beds()
LS0tCnRpdGxlOiAiSG9zcGl0YWwgRW50cnkgU2ltdWxhdGlvbiIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyMjQmFja2dyb3VuZDoKCkxldCdzIHRha2UgYSBzaW1wbGUgbW9kZWwgb2YgYSB0aHJlZSBkZXBhcnRtZW50IGhvc3BpdGFsLCB3aXRoIGFuIEVtZXJnZW5jeSBEZXBhcnRtZW50IChFRCksIGFuIEludGVuc2l2ZSBDYXJlIFVuaXQgKElDVSksIGFuZCBhIGRlcGFydG1lbnQgZm9yIEdlbmVyYWwgKEdlbikgQWRtaXNzaW9uIGZvciBsZXNzIHNldmVyZSBpbmp1cmllcy4gV2UgY2FuIHBlcmZvcm0gYXMgc2ltdWxhdGlvbiBvdmVyIHRoZSBjb3Vyc2Ugb2YgYSB5ZWFyIGFuZCBzZWUgaG93IG1hbnkgcGF0aWVudHMgd2UgY2FuIGN1cmUsIHdoaWxlIG1pbmltaXppbmcgdGhlIGFtb3VudCB3ZSBoYXZlIHRvIHRyYW5zZmVyLCBhbmQgd2hvIHBhc3MgYXdheS4KCgpTaW11bGF0aW9ucyBhcmUgZ2VuZXJhbGx5IGZ1bmN0aW9uLWhlYXZ5LiBUaGlzIGlzIGJlY2F1c2UgaXQgYWxsb3dzIHVzIHBlcmZvcm0gdGhlIHNhbWUgb3BlcmF0aW9uIHdpdGhvdXQgdW5uZWNlc3NhcnkgY29weS1wYXN0aW5nIG9mIGNvZGUuIFNvIGxldCdzIHB1dCBvdXIgaW5pdGlhbCBzZXQtdXAgb2YgdmFyaWFibGVzLCB3aGljaCBoYXZlIGJlZW4gbW9yZS1vci1sZXNzIGFyYml0cmFyaWx5IGNob3NlbiwgaW50byBhIGZ1bmN0aW9uCgpgYGB7cn0KbGlicmFyeShkcGx5cikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGdnYW5pbWF0ZSkKCmluaXRpYWxfc2V0dXAgICA8PC0gZnVuY3Rpb24oKXsKICBlZF9iZWRzLmkgICAgICAgPDwtIDM1ICMgRW1lcmdlbmN5IERlcGFydG1lbnQgYmVkcyBhdmFpbGFibGUKICBmdWxsX2VkX2JlZHMuaSAgPDwtIDAgICMgT2NjdXBpZWQgRW1lcmdlbmN5IERlcGFydG1lbnQgYmVkcwogICAgCiAgaWN1X2JlZHMuaSAgICAgIDw8LSA1MCAjIElDVSBEZXBhcnRtZW50IGJlZHMgYXZhaWxhYmxlCiAgZnVsbF9pY3VfYmVkcy5pIDw8LSAwICAgIyBPY2N1cGllZCBJQ1UgRGVwYXJ0bWVudCBiZWRzCiAgCiAgZ2VuX2JlZHMuaSAgICAgIDw8LSA1MCAgIyBHZW5lcmFsIERlcGFydG1lbnQgYmVkcyBhdmFpbGFibGUKICBmdWxsX2dlbl9iZWRzLmkgPDwtIDAgICAjIE9jY3VwaWVkIEdlbmVyYWwgRGVwYXJ0bWVudCBiZWRzCiAgCiAgdG90YWxfcGF0aWVudHMgIDw8LSA1MDAwCiAgZmluYWxfZGF5ICAgICAgIDw8LSAzNjUgIyBmaW5hbCBkYXkgb2Ygb3VyIHNpbXVsYXRpb24KICBkYXkudiAgICAgICAgICAgPDwtIHNlcSgxOmZpbmFsX2RheSkKCn0KCmluaXRpYWxfc2V0dXAoKQpgYGAKCgpXZSdsbCBuZWVkIGEgZGF0YSBmcmFtZSB0byBob2xkIGFsbCBvZiBvdXIgcGF0aWVudCBkYXRhLiBGb3IgdGhlIHNha2Ugb2YgZWZmaWNpZW5jeSwgd2UgZ2VuZXJhdGUgYWxsIG9mIHRoaXMgZGF0YSB1cCBmcm9udC4KCmBgYHtyfQpwYXRpZW50LmRmX3NldHVwIDwtIGZ1bmN0aW9uKCl7CiAgCiAgZW50cnlfZGF5LnYgIDwtIHNvcnQoc2FtcGxlKDA6ZmluYWxfZGF5LCB0b3RhbF9wYXRpZW50cywgcmVwbGFjZT1UUlVFKSkgIyBUaGUgZGF5IHRoYXQgdGhlIHBhdGllbnQgaXMgaW5qdXJlZAogIHNldmVyaXR5LnYgICA8LSBydW5pZihuID0gdG90YWxfcGF0aWVudHMsIG1pbiA9IC4xLCBtYXggPSAxKSAjIFRoZSBsZXZlbCBvZiBzZXZlcml0eSBvZiBwYXRpZW50IGluanVyeQogIGxvY2F0aW9uLnYgICA8LSByZXAoIkhvbWUiLCB0b3RhbF9wYXRpZW50cykgICAgIyBDdXJyZW50IGxvY2F0aW9uIG9mIHBhdGllbnRzCiAgc3RhdHVzLnYgICAgIDwtIHJlcCgiSGVhbHRoeSIsIHRvdGFsX3BhdGllbnRzKSAjIG90aGVyIGNhdGFnb3JpZXMgd2lsbCBiZSBJbmp1cmVkLCBSZWxlYXNlZCwgRGVjZWFzZWQsIEN1cmVkCiAgcGxhY2VtZW50LnYgIDwtIGlmZWxzZShzZXZlcml0eS52ID49IDAuOCwgICJFRCIsCiAgICAgICAgICAgICAgICAgIGlmZWxzZShzZXZlcml0eS52ID4gMC42LCAiSUNVIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHZW4iKSkgIyBUaGUgaW5pdGlhbCBwbGFjZW1lbnQgb2YgcGF0aWVudCBiYXNlZCBvbiBzZXZlcml0eQogIAogIGRmIDwtIGRhdGEuZnJhbWUoIkVudHJ5X2RheSIgPSBlbnRyeV9kYXkudiwgIlNldmVyaXR5IiA9IHNldmVyaXR5LnYsIAogICAgICAgICAgICAgICAgICAgIlBsYWNlbWVudCIgPSBwbGFjZW1lbnQudiwgIkxvY2F0aW9uIiA9IGxvY2F0aW9uLnYsCiAgICAgICAgICAgICAgICAgICAiU3RhdHVzIiAgICA9IHN0YXR1cy52KQogIGRmIDwtIGRmICU+JSBtdXRhdGUoUGxhY2VtZW50ID0gYXMuY2hhcmFjdGVyKFBsYWNlbWVudCksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTG9jYXRpb24gID0gYXMuY2hhcmFjdGVyKExvY2F0aW9uKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0YXR1cyAgICA9IGFzLmNoYXJhY3RlcihTdGF0dXMpKQogICAgIAogIGRmIDwtIGRmICU+JSBhcnJhbmdlKEVudHJ5X2RheSkKCiAgcmV0dXJuKGRmKSAgICAgICAgICAgICAgICAgICAgICAgICAKfQpwYXRpZW50LmRmIDwtIHBhdGllbnQuZGZfc2V0dXAoKQpgYGAKCgpOb3cgYmVmb3JlIHdlIGFjdHVhbGx5IGJlZ2luIG91ciBzaW11bGF0aW9uLCBsZXQncyBicmVhayBpdCBpbnRvIHBpZWNlcyBmb3IgdGhlIHNha2Ugb2YgbWFpbnRhaW5hYmxlIGNvZGUuIFRoaXMgbWF5IGxvb2sgYSBiaXQgc3RyYW5nZSBhdCBmaXJzdCwgYnV0IGl0IHdpbGwgYWxsIGNvbWUgdG9nZXRoZXIgYXMgd2UgbW92ZSB0b3dhcmQgdGhlIGVuZC4gCgoKVGhlIGZpcnN0IGZ1bmN0aW9uIHdlJ2xsIGxvb2sgYXQgaGFuZGxlcyBuZXcgcGF0aWVudHMgZW50ZXJpbmcgdGhlIGhvc3BpdGFsLiBUaGV5IGFyZSBzb3J0ZWQgaW50byB0aGVpciBhcHByb3ByaWF0ZSBkZXBhcnRtZW50LCBhbmQgdGhlaXIgc3RhdHVzIGFuZCBsb2NhdGlvbiBhcmUgYWRqdXN0ZWQgYXBwcm9wcmlhdGVseS4KCgpgYGB7cn0KZW50cmFuY2UgPC0gZnVuY3Rpb24oZGYsIGRheSl7ICMgRW50cmFuY2Ugb2YgbmV3IHBhdGllbnRzIGZvciB0aGUgZGF5CiAgCiAgZGYgPC0gZGYgJT4lICBtdXRhdGUoUGxhY2VtZW50ID0gaWZlbHNlKFNldmVyaXR5ID49IDAuOCwgICJFRCIsIGlmZWxzZShTZXZlcml0eSA+IDAuNiwgIklDVSIsICJHZW4iKSksCiAgICAgICAgICAgICAgICAgICAgICAgU3RhdHVzICAgID0gaWZlbHNlKChFbnRyeV9kYXkgPT0gZGF5KSAmIChMb2NhdGlvbiA9PSAiSG9tZSIpLCJJbmp1cmVkIiwgU3RhdHVzKSwKICAgICAgICAgICAgICAgICAgICAgICBMb2NhdGlvbiAgPSBpZmVsc2UoKFN0YXR1cyA9PSAiSW5qdXJlZCIpICYgKExvY2F0aW9uID09ICJIb21lIiksUGxhY2VtZW50LCBMb2NhdGlvbikpICU+JQogICAgICAgICAgICAgICAgYXJyYW5nZShFbnRyeV9kYXksU2V2ZXJpdHkpCiAgcmV0dXJuKGRmKQogIH0KYGBgCgoKT3VyIHNlY29uZCBmdW5jdGlvbiBpcyBnb2luZyB0byBhZGp1c3Qgb3VyIHBhdGllbnQncyAiU2V2ZXJpdHkiIHZhbHVlIG9uIGEgZGFpbHkgYmFzaXMuIFRoZSBwYXRpZW50J3MgY29uZGl0aW9uIGNhbiBpbXByb3ZlLCBzdGF5IGFib3V0IHRoZSBzYW1lLCBvciBkZXRlcmlvcmF0ZS4gVGhlIGJldGEgZGlzdHJpYnV0aW9uIGhhcyB0aGUgYXBwcm9wcmlhdGUgcXVhbGl0aWVzIHRvIGZpdCBvdXIgbW9kZWwsIGJ1dCBpdCdzIGltcG9ydGFudCB0byBub3RlIHRoYXQgeW91IGNvdWxkIGVhc2lseSBkZWZlbmQgdXNpbmcgbWFueSBvdGhlciBkaXN0cmlidXRpb25zIGZvciB0aGlzLgoKV2hlbiB3ZSBjYWxsIHRoaXMgZnVuY3Rpb24gbGF0ZXIsIHdlJ3JlIGdvaW5nIHRvIHN0YXkgb3B0aW1pc3RpYyBhbmQgc2F5IHRoYXQgcGF0aWVudHMgd2lsbCBnZW5lcmFsbHkgaW1wcm92ZSBkdXJpbmcgdGhlaXIgc3RheSBpbiB0aGUgaG9zcGl0YWwuCgpgYGB7cn0KaW1wcm92ZW1lbnRzIDwtIGZ1bmN0aW9uKGRmLCBza2V3ID0gIk5vbmUiKXsgIyBBZGp1c3RzIHBhdGllbnQgc2V2ZXJpdHkgYnkgYSByYW5kb20gdmFsdWUgZHJhd24gZnJvbSBiZXRhIGRpc3RyaWJ1dGlvbgoKICAjIEJldGEgZGlzdHJpYnV0aW9uIHBhcmFtZXRlcnMKICBpZiAgICAgIChza2V3ID09ICJMZWZ0IiApe3BhcmFtMSA9IDQwICMgTGVmdCBza2V3ZWQgLS0tIFBhdGllbnQgd2lsbCBnZW5lcmFsbHkgaW1wcm92ZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFyYW0yID0gNTB9IAogIGVsc2UgaWYgKHNrZXcgPT0gIlJpZ2h0Iil7cGFyYW0xID0gNTAgIyBSaWdodCBza2V3ZWQgLS0tIFBhdGllbnQgd2lsbCBnZW5lcmFsbHkgZGV0ZXJpb3JhdGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhcmFtMiA9IDQwfQogIGVsc2UgICAgICAgICAgICAgICAgICAgICB7cGFyYW0xID0gNDUgIyBObyBza2V3IC0tLSBQYXRpZW50IGhhcyBhbiBlcXVhbCBwcm9iYWJpbGl0eSB0byBpbXByb3ZlL2RldGVyaW9yYXRlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXJhbTIgPSA0NX0KICAgICAgICAgICAgICAgICAgCiAgZGYgPC0gZGYgJT4lIG11dGF0ZShTZXZlcml0eSA9IGlmZWxzZSggTG9jYXRpb24gPT0gIkVEIiwgIFNldmVyaXR5ICsgKHJiZXRhKDEsIHBhcmFtMSwgcGFyYW0yKSAtIC41KSAqIDEuNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKCBMb2NhdGlvbiA9PSAiSUNVIiwgU2V2ZXJpdHkgKyAocmJldGEoMSwgcGFyYW0xLCBwYXJhbTIpIC0gLjUpICogMS4yNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKCBMb2NhdGlvbiA9PSAiR2VuIiwgU2V2ZXJpdHkgKyAocmJldGEoMSwgcGFyYW0xLCBwYXJhbTIpIC0gLjUpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTZXZlcml0eSkpKSwKICAgICAgICAgICAgICAgICAgICAgIExvY2F0aW9uID0gaWZlbHNlKCBTZXZlcml0eSA8PSAwLCAiSG9tZSIsICAjIFBhdGllbnQgaXMgY3VyZWQgYW5kIHNlbnQgaG9tZS4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKCBTZXZlcml0eSA+PSAxLCAiTW9yZ3VlIiwjIFBhdGllbnQgcGFzc2VzIGF3YXkgYW5kIGlzIHNlbnQgdG8gdGhlIG1vcmd1ZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIExvY2F0aW9uKSksCiAgICAgICAgICAgICAgICAgICAgICBTdGF0dXMgICA9IGlmZWxzZSggU2V2ZXJpdHkgPD0gMCwgIkN1cmVkIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZSggU2V2ZXJpdHkgPj0gMSwgIkRlY2Vhc2VkIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RhdHVzKSkpCgpyZXR1cm4oZGYpCn0KYGBgCgoKVGhlIHRoaXJkIGFuZCBmb3VydGggZnVuY3Rpb25zIGFyZSBzaW1wbHkgZ29pbmcgdG8gc2VlIGhvdyBtYW55IHBhdGllbnRzIGFyZSBpbiBlYWNoIGxvY2F0aW9uLCBhbmQgd2hpY2ggc3RhdHVzIHRoZXkgYXJlIHByZXNlbnRpbmcuIEF0IHRoZSBlbmQgb2YgZWFjaCBkYXksIHRoZXNlIGFyZSBwdXQgaW50byBhIGRhdGEgZnJhbWUgZm9yIGxhdGVyIHZpc3VhbGl6YXRpb24uCgoKYGBge3J9CnRhbGx5X2JlZHMgPC0gZnVuY3Rpb24oZGYpeyAjIGdldHMgY3VycmVudCBudW1iZXIgb2YgZnVsbCBiZWRzCiAgCiAgb2xkX2RlY2Vhc2VkLmkgICAgPDwtIGRlY2Vhc2VkLmkKICBvbGRfdHJhbnNmZXJyZWQuaSA8PC0gdHJhbnNmZXJyZWQuaQogIAogIGZ1bGxfZWRfYmVkcy5pICA8PC0gdGFsbHkoZGYgJT4lIGZpbHRlcihMb2NhdGlvbiA9PSAiRUQiKSkgJT4lIGFzLmludGVnZXIoKQogIGZ1bGxfaWN1X2JlZHMuaSA8PC0gdGFsbHkoZGYgJT4lIGZpbHRlcihMb2NhdGlvbiA9PSAiSUNVIikpICU+JSBhcy5pbnRlZ2VyKCkKICBmdWxsX2dlbl9iZWRzLmkgPDwtIHRhbGx5KGRmICU+JSBmaWx0ZXIoTG9jYXRpb24gPT0gIkdlbiIpKSAlPiUgYXMuaW50ZWdlcigpCiAgZGVjZWFzZWQuaSAgICAgIDw8LSB0YWxseShkZiAlPiUgZmlsdGVyKFN0YXR1cyA9PSAiRGVjZWFzZWQiKSkgJT4lIGFzLmludGVnZXIoKQogIGN1cmVkLmkgICAgICAgICA8PC0gdGFsbHkoZGYgJT4lIGZpbHRlcihTdGF0dXMgPT0gIkN1cmVkIikpICU+JSBhcy5pbnRlZ2VyKCkKICBoZWFsdGh5LmkgICAgICAgPDwtIHRhbGx5KGRmICU+JSBmaWx0ZXIoU3RhdHVzID09ICJIZWFsdGh5IikpICU+JSBhcy5pbnRlZ2VyKCkKICB0cmFuc2ZlcnJlZC5pICAgPDwtIHRhbGx5KGRmICU+JSBmaWx0ZXIoU3RhdHVzID09ICJUcmFuc2ZlcnJlZCIpKSAlPiUgYXMuaW50ZWdlcigpCiAgaGVhbGh0eS5pICAgICAgIDw8LSB0YWxseShkZiAlPiUgZmlsdGVyKFN0YXR1cyA9PSAiSGVhbHRoeSIpKSAlPiUgYXMuaW50ZWdlcigpCn0KYGBgCgpgYGB7cn0KZGF5X2VuZF90YWxseSA8LSBmdW5jdGlvbihkZiwgZGF5KXsKICB0YWxseV9iZWRzKGRmKQogIAogIGZ1bGxfYmVkcy5kZiA8PC0gcmJpbmQoZnVsbF9iZWRzLmRmLCBkYXRhLmZyYW1lKERheSAgICAgICAgID0gZGF5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEVEICAgICAgICAgID0gZnVsbF9lZF9iZWRzLmksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSUNVICAgICAgICAgPSBmdWxsX2ljdV9iZWRzLmksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdlbiAgICAgICAgID0gZnVsbF9nZW5fYmVkcy5pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEhlYWx0aHkgICAgID0gaGVhbHRoeS5pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEN1cmVkICAgICAgID0gY3VyZWQuaSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBEZWNlYXNlZCAgICA9IGRlY2Vhc2VkLmksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhbnNmZXJyZWQgPSB0cmFuc2ZlcnJlZC5pKSkKfQoKYGBgCgoKVGhlIHJlcmFuayBmdW5jdGlvbiBzaW1wbHkgZ2l2ZXMgb3VyIGxvd2VzdCBzZXZlcml0eSBwYXRpZW50cyAocGVyIGdyb3VwKSBhIHJhbmsgY2xvc2VyIHRvIHplcm8uIFdoeSBkbyB3ZSBuZWVkIHRoYXQ/IEJlY2F1c2UgdGhlIGhvc3BpdGFsLCB3aGVuIGZhY2VkIHdpdGggc3BhY2UgY29uc3RyYWludHMsIHdpbGwgdHJ5IHRvIHJlbGVhc2Ugb3IgdHJhbnNmZXIgdGhlaXIgbG93ZXN0IHNldmVyaXR5IHBhdGllbnRzICh3aXRoIHNvbWUgY2F2ZWF0cyBiZWxvdykuIAoKYGBge3J9CnJlcmFuayA8LSBmdW5jdGlvbiAoZGYpeyAjIFJhbmtzIHBhdGllbnQgU2V2ZXJpdHkgYnkgbG9jYXRpb24gLS0tIGxvd2VzdCBTZXZlcml0eSBnZXRzIGxvd2VzdCBudW1iZXIgcmFuawogIGRmIDwtIGRmICU+JSBncm91cF9ieShMb2NhdGlvbikgJT4lCiAgICAgICAgICAgICAgIG11dGF0ZShSYW5rID0gaWZlbHNlKExvY2F0aW9uICVpbiUgYygiRUQiLCJJQ1UiLCJHZW4iKSwgcmFuayhTZXZlcml0eSwgdGllcy5tZXRob2QgPSAiZmlyc3QiKSxOQSkpICU+JQogICAgICAgICAgICAgICB1bmdyb3VwKCkKICB0YWxseV9iZWRzKGRmKQogIHJldHVybihkZikKICB9CmBgYAoKCkJlbG93IGlzIG91ciBtYWluIGZ1bmN0aW9uLiBJdCBzaW11bGF0ZXMgb25lIGRheSBieSBoYXZpbmcgbmV3IHBhdGllbnRzIGVudGVyLCB0cmFuc2ZlcnJpbmcgcGF0aWVudHMgYXMgbmVlZGVkLCB0YWxseWluZyB1cCB0aGUgYmVkcywgYWRqdXN0aW5nIG91ciBwYXRpZW50J3Mgc2V2ZXJpdHkgZWFjaCBkYXksIGFuZCB0aGVuIHRoZSBmdW5jdGlvbiB3aWxsIHJlY3Vyc2l2ZWx5IGNhbGwgaXRzZWxmIHVudGlsIGl0IGhpdHMgdGhlIGVuZCBvZiB0aGUgc2ltdWxhdGlvbi4KCkZvciB0aGUgc2FrZSBvZiB0aGUgbW9kZWwsIHdlIG9ubHkgYWxsb3cgdHJhbnNmZXJzIHRvIG90aGVyIGRlcGFydG1lbnRzIGlmIHRoZWlyIHNldmVyaXR5IGlzIG9uIHRoZSBib3JkZXIgYmV0d2VlbiB0aGUgZXN0YWJsaXNoZWQgcG9pbnRzLiBGb3IgZXhhbXBsZSwgYSBwYXRpZW50IG11c3QgYmUgYmV0d2VlbiBzZXZlcml0eSBsZXZlbCAwLjggYW5kIDAuODUgdG8gYmUgY29uc2lkZXJlZCBmb3IgdHJhbnNmZXIgZnJvbSB0aGUgRW1lcmdlbmN5IERlcGFydG1lbnQgdG8gdGhlIElDVS4gSWYgdGhlIGhvc3BpdGFsIHN0aWxsIGNhbid0IGhhbmRsZSB0aGUgbnVtYmVyIG9mIHBhdGllbnRzLCB0aGV5IGFyZSB0cmFuc2ZlcnJlZCB0byBhbm90aGVyIGhvc3BpdGFsLgoKCmBgYHtyfQoKbmV4dF9kYXkgPC0gZnVuY3Rpb24oZGYsIGRheSwgZmluYWxfZGF5KXsgICMgUmVjdXJzaXZlIGZ1bmN0aW9uIC0tLSByZWFzc2lnbnMgcGF0aWVudHMgdG8gbmV3IGFyZWEgaWYgYmVkcyBmdWxsCiAgCiAgZGYgPC0gZW50cmFuY2UoZGYsIGRheSkgCiAgZGYgPC0gcmVyYW5rKGRmKQogIAogICMgcmVhc3NpZ24gRUQgcGF0aWVudHMgdG8gSUNVIGRlcGFydG1lbnQgaWYgRUQgaXMgZnVsbCBpZiBwb3NzaWJsZQogICMgaWYgcGF0aWVudCBpcyB0b28gc2V2ZXJlIGZvciBJQ1UgLSB0aGV5IGFyZSB0cmFuc2ZlcmVkIHRvIGFub3RoZXIgaG9zcGl0YWwKICBkZiA8LSBkZiAlPiUgbXV0YXRlKExvY2F0aW9uID0gaWZlbHNlKChMb2NhdGlvbiA9PSAiRUQiKSAgJiAoUmFuayA8PSAoZnVsbF9lZF9iZWRzLmkgLSBlZF9iZWRzLmkpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFNldmVyaXR5IDwgMC44NSwiSUNVIiwiQW5vdGhlciBIb3NwaXRhbCIpLCBMb2NhdGlvbikpICAKICBkZiA8LSByZXJhbmsoZGYpCiAgCiAgCiAgIyByZWFzc2lnbiBJQ1UgcGF0aWVudHMgdG8gR2VuIGRlcGFydG1lbnR0IGlmIElDVSBpcyBmdWxsCiAgIyBpZiBwYXRpZW50IGlzIHRvbyBzZXZlcmUgZm9yIEdlbmVyYWwgYWRtaXNzaW9uIC0gdGhleSBhcmUgdHJhbnNmZXJlZCB0byBhbm90aGVyIGhvc3BpdGFsCiAgZGYgPC0gZGYgJT4lIG11dGF0ZShMb2NhdGlvbiA9IGlmZWxzZSgoTG9jYXRpb24gPT0gIklDVSIpICYgKFJhbmsgPD0gKGZ1bGxfaWN1X2JlZHMuaSAtIGljdV9iZWRzLmkpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFNldmVyaXR5IDwgMC42NSwiR2VuIiwiQW5vdGhlciBIb3NwaXRhbCIpLCBMb2NhdGlvbikpCiAgCiAgZGYgPC0gcmVyYW5rKGRmKQogIAogICMgcmVsYXNlcyBwYXRpZW50cyBpZiBiZWRzIGFyZSBmdWxsIGFuZCB0aGVpciBzZXZlcml0eSBpcyBsb3cgZW5vdWdoCiAgIyBvdGhlcndpc2UgdHJhbnNmZXJzIHRoZW0gdG8gYW5vdGhlciBob3NwaXRhbAogIGRmIDwtIGRmICU+JSBtdXRhdGUoTG9jYXRpb24gPSBpZmVsc2UoKExvY2F0aW9uID09ICJHZW4iKSAmIChSYW5rIDw9IChmdWxsX2dlbl9iZWRzLmkgLSBnZW5fYmVkcy5pKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShTZXZlcml0eSA8IDAuMjAsIlJlbGVhc2VkIiwiQW5vdGhlciBIb3NwaXRhbCIpLCBMb2NhdGlvbiksCiAgICAgICAgICAgICAgICAgICAgICBTdGF0dXMgICA9IGlmZWxzZShMb2NhdGlvbiA9PSAiQW5vdGhlciBIb3NwaXRhbCIsICJUcmFuc2ZlcnJlZCIsIFN0YXR1cyksCiAgICAgICAgICAgICAgICAgICAgICBTdGF0dXMgICA9IGlmZWxzZShMb2NhdGlvbiA9PSAiUmVsZWFzZWQiLCAiUmVsZWFzZWQiLCBTdGF0dXMpKQoKICBkYXlfZW5kX3RhbGx5KGRmLGRheSkKICBkZiA8LSBpbXByb3ZlbWVudHMoZGYsIHNrZXcgPSAiTGVmdCIpICMgQWRqdXN0cyBwYXRpZW50cyBzZXZlcml0eSBmb3IgdGhlIGRheQogIAogIGRheSA9IGRheSArIDEKICBpZihkYXkgPD0gZmluYWxfZGF5KXsgIAogICAgbmV4dF9kYXkgKGRmLCBkYXksIGZpbmFsX2RheSkgIyBNb3ZlIG9uIHRvIG5leHQgZGF5IGlmIG5vdCBwYXN0IHRoZSBmaW5hbCBkYXkKICAgIH0KICBlbHNlIHtyZXR1cm4oZGYpfQp9CgpgYGAKCkFuZCBvdXIgbGFzdCBmdW5jdGlvbiBkb2luZyBhbnkgYW5hbHl0aWMgd29yayB3aWxsIGNhbGwgdGhlIGZ1bmN0aW9ucyBhYm92ZSBhbmQgZ2l2ZSB1cyBvdXIgZGF0YSBpbiBhIG5pY2UgZm9ybWF0IGZvciBvdXIgc2ltdWxhdGlvbiB2aXN1YWxpemF0aW9uLgoKYGBge3J9CmJlZF9jb3VudCA8LSBmdW5jdGlvbigpewogIGZ1bGxfYmVkcy5kZiA8PC0gZGF0YS5mcmFtZShEYXkgICAgICAgICA9IGludGVnZXIoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRUQgICAgICAgICAgPSBpbnRlZ2VyKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIElDVSAgICAgICAgID0gaW50ZWdlcigpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgR2VuICAgICAgICAgPSBpbnRlZ2VyKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEhlYWx0aHkgICAgID0gaW50ZWdlcigpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDdXJlZCAgICAgICA9IGludGVnZXIoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRGVjZWFzZWQgICAgPSBpbnRlZ2VyKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYW5zZmZlcmVkID0gaW50ZWdlcigpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBIZWFsdGh5ICAgICA9IGludGVnZXIoKSkKICAKICBkZWNlYXNlZC5pICAgIDw8LSAwCiAgdHJhbnNmZXJyZWQuaSA8PC0gMAogIAogIHBhdGllbnQuZGYgPDwtIG5leHRfZGF5KHBhdGllbnQuZGYsIDAsIGZpbmFsX2RheSkKICB0YWxseV9iZWRzKHBhdGllbnQuZGYpCn0KYmVkX2NvdW50KCkKZnVsbF9iZWRzLmRmCgpgYGAKQWJvdmUsIGluIHRhYnVsYXIgZm9ybSwgd2UgY2FuIHNlZSBob3cgb3VyIGRlcGFydG1lbnRzIGFuZCBzdGF0dXNlcyBvZiBvdXIgcGF0aWVudHMgY2hhbmdlIGVhY2ggZGF5LiBCdXQgdGhlcmUgaXMgb25seSBzbyBtdWNoIGRhdGEgdGhhdCBjYW4gYmUgYWJzb3JiZWQgdGhyb3VnaCBhIHRhYmxlLiBJbiBvcmRlciBmb3IgdGhlc2UgZGF0YSB0byBiZSBtZWFuaW5nZnVsLCB3ZSBoYXZlIHRvIHVuZGVyc3RhbmQgaXQgb24gYSBkZWVwZXIgbGV2ZWwgdGhhbiBjYW4gYmUgY29udmV5ZWQgYnkgYSBzdGF0aWMgbnVtYmVyIG9uIGEgcGFnZS4KCkFuZCBmb3IgdGhhdCwgd2UgdXNlIHZpc3VhbGl6YXRpb25zLiBVc2luZyB0aGUgZ2dhbmltYXRlIHBhY2thZ2UsIGFuZCB0aGUgZGF0YSBnZW5lcmF0ZWQgYWJvdmUsIHdlIGNhbiBzaG93IGhvdyB0aGUgY29tcG9zaXRpb24gb2Ygb3VyIGhvc3BpdGFsIGNoYW5nZXMgb3ZlciB0aGUgY291cnNlIG9mIGEgeWVhci4KCmBgYHtyfQpwbG90X2Z1bGxfYmVkcyA8LSBmdW5jdGlvbigpewogIG9wdGlvbnMoZ2dhbmltYXRlLm5mcmFtZXMgPSBtYXgoZnVsbF9iZWRzLmRmJERheSkqMikKICBmdWxsX2JlZHMuZ2cgPC1nZ3Bsb3QoZnVsbF9iZWRzLmRmKSArIAogICAgICAgICAgICAgICAgIGdlb21fcG9pbnQoYWVzKDIsIC0wLjMsICBzaXplID0gSGVhbHRoeSksIGNvbG9yID0gInJveWFsYmx1ZSIpICsKICAgICAgICAgICAgICAgICBnZW9tX3BvaW50KGFlcygxLCAgIDEsICAgc2l6ZSA9IEVEKSwgIGNvbG9yID0gImRlZXBza3libHVlIikgKyAKICAgICAgICAgICAgICAgICBnZW9tX3BvaW50KGFlcygyLCAgIDEsICAgc2l6ZSA9IElDVSksIGNvbG9yID0gImRlZXBza3libHVlIikgKwogICAgICAgICAgICAgICAgIGdlb21fcG9pbnQoYWVzKDMsICAgMSwgICBzaXplID0gR2VuKSwgY29sb3IgPSAiZGVlcHNreWJsdWUiKSArCiAgICAgICAgICAgICAgICAgZ2VvbV9wb2ludChhZXMoMi41LCAzLCAgIHNpemUgPSBEZWNlYXNlZCksIGNvbG9yID0gInN0ZWVsYmx1ZSIpICsKICAgICAgICAgICAgICAgICBnZW9tX3BvaW50KGFlcygyLCAgIDIsICAgc2l6ZSA9IFRyYW5zZmVycmVkKSwgY29sb3IgPSAic3RlZWxibHVlIikgKwogICAgICAgICAgICAgICAgIGdlb21fcG9pbnQoYWVzKDEuNSwgMywgICBzaXplID0gQ3VyZWQpLCBjb2xvciA9ICJzdGVlbGJsdWUiKSArCiAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgZ2VvbV90ZXh0KGFlcygyLCAgLTAuNSwgbGFiZWwgPSBwYXN0ZSgiSGVhbHRoeVxuIiwgSGVhbHRoeSkpKSArIAogICAgICAgICAgICAgICAgIGdlb21fdGV4dChhZXMoMSwgICAwLjcsIGxhYmVsID0gcGFzdGUoIkVEXG4iLCBFRCkpKSArCiAgICAgICAgICAgICAgICAgZ2VvbV90ZXh0KGFlcygyLCAgIDAuNywgbGFiZWwgPSBwYXN0ZSgiSUNVXG4iLCBJQ1UpKSkgKwogICAgICAgICAgICAgICAgIGdlb21fdGV4dChhZXMoMywgICAwLjcsIGxhYmVsID0gcGFzdGUoIkdlblxuIiwgR2VuKSkpICsKICAgICAgICAgICAgICAgICBnZW9tX3RleHQoYWVzKDIuNSwgMi43LCBsYWJlbCA9IHBhc3RlKCJEZWNlYXNlZFxuIiwgRGVjZWFzZWQpKSkgKyAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICBnZW9tX3RleHQoYWVzKDIsICAgMS43LCBsYWJlbCA9IHBhc3RlKCJUcmFuc2ZlcnJlZFxuIiwgVHJhbnNmZXJyZWQpKSkgKyAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICBnZW9tX3RleHQoYWVzKDEuNSwgMi43LCBsYWJlbCA9IHBhc3RlKCJDdXJlZFxuIiwgQ3VyZWQpKSkgKyAKICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICB0cmFuc2l0aW9uX3N0YXRlcyhEYXksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJhbnNpdGlvbl9sZW5ndGggPSAyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YXRlX2xlbmd0aCA9IDIpICsKICAgICAgICAgICAgICAgICBzY2FsZV9zaXplKHJhbmdlID0gYygxLDgwKSkgKwogICAgICAgICAgICAgICAgIHRoZW1lX2J3KCkgKwogICAgICAgICAgICAgICAgIHRoZW1lKGF4aXMubGluZT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpY2tzPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLng9ZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uPSJub25lIiwKICAgICAgICAgICAgICAgICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgICAgICBwYW5lbC5ib3JkZXI9ZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICAgIHBhbmVsLmdyaWQubWlub3I9ZWxlbWVudF9ibGFuaygpKSArCiAgICAgICAgICAgICAgICAgeGxpbShjKCAwLjUsIDMuNSkpICsKICAgICAgICAgICAgICAgICB5bGltKGMoIC0xLCAgMy41KSkgKwogICAgICAgICAgICAgICAgIGdndGl0bGUoIkRheSB7Y2xvc2VzdF9zdGF0ZX0iKSArCiAgICAgICAgICAgICAgICAgdGhlbWUoKQogIHByaW50KGZ1bGxfYmVkcy5nZykKICBhbmltX3NhdmUoIkhvc3BpdGFsX3NpbXVsYXRpb24uZ2lmIiwgZnVsbF9iZWRzLmdnKQp9CgpwbG90X2Z1bGxfYmVkcygpCgpgYGAKIyFbXShIb3NwaXRhbF9zaW11bGF0aW9uLmdpZikKCgojIyMgQ29uY2x1c2lvbnM6CgpUaGUgYmFzZSBsb2dpYyBvZiB0aGUgbW9kZWwgaGFzIHdpZGUgYXBwbGljYXRpb25zLCBhbmQgbm90IGp1c3QgaW4gdGhlIG1lZGljYWwgZmllbGQuIFdlIGNvdWxkIHRha2UgdGhlIG1vZGVsIGZhciBvdXQgb2YgaXRzIG9yaWdpbmFsIHB1cnBvc2UsIGFuZCB3aXRoIG1pbm9yIG1vZGlmaWNhdGlvbnMgdGhpcyBjb3VsZCwgZm9yIGV4YW1wbGUsIHNlcnZlIGFzIGEgd2F5IHRvIHNvcnQgYW5kIHRyYW5zZmVyIGNoaWxkcmVuIGludG8gZGlmZmVyZW50IGNsYXNzZXMuCgoKT3ZlcmFsbCwgdGhpcyBtb2RlbCBpcyBhIHNpbXBsZSBvbmUsIGFuZCB0aGF0IGlzIGludGVudGlvbmFsLiBNYW55IHBvc3NpYmxlIGFkZGl0aW9ucyBjb21lIHRvIG1pbmQgd2hlbiBsb29raW5nIGF0IHRoaXMgbW9kZWwuIFdlIGNvdWxkIGFkZCBkb2N0b3JzLCBudXJzZXMsIGFuZCBhZG1pbmlzdHJhdG9ycy4gV2UgY291bGQgdHJ5IGRpZmZlcmVudCBhbW91bnRzIG9mIGJlZHMgaW4gZWFjaCBhcmVhLiBXZSBjb3VsZCBhZGQgYnVkZ2V0IGNvbnN0cmFpbnRzLiBXZSBjb3VsZCBoYXZlIGEgd2FpdGluZyByb29tLCBvciBoYXZlIHRoZSBzaW11bGF0aW9uIHRpY2sgYXdheSBhdCBhbiBob3VybHkgcmF0aGVyIHRoYW4gZGFpbHkgYmFzaXMsIG9yIHRyeSBkaWZmZXJlbnQgZGlzdHJpYnV0aW9ucyBmb3Igc2V2ZXJpdHkgY2hhbmdlLCBvciBoYXZlIHRoZSBhYmlsaXR5IHRvIHNldCB1cCB0ZW1wb3JhcnkgdHJpYWdlIGNlbnRlcnMgZm9yIGVtZXJnZW5jaWVzLCBvci4uLiBhbnkgb25lIG9mIGEgbWlsbGlvbiBkaWZmZXJlbnQgdGhpbmdzLiAKCkJ1dCBoZXJlLCB0aGUgYmlnZ2VzdCBjb25zdHJhaW50IGlzIG5vdCB0aGluZ3MgdGhhdCBjb3VsZCBiZSBhZGRlZCB0byB0aGUgc2ltdWxhdGlvbi4gSW5zdGVhZCwgdGhlIHByb2JsZW0gaXMgdGhlIHVzdWFsIGN1bHByaXQsIG5vdCBlbm91Z2ggZ29vZCBkYXRhLiBXaXRob3V0IGFjY3VyYXRlIGRhdGEsIHdlIHdvdWxkIGxpa2VseSBzb29uIGZpbmQgdGhhdCBhIG1vcmUgY29tcGxpY2F0ZWQgc2ltdWxhdGlvbiB3b3VsZCBtYWtlIGl0ICBpbXBvc3NpYmxlIHRvIGZvcm0gcHJvcGVyIGNvbmNsdXNpb25zLgoKCgo=